﻿using Common;
using HslCommunication;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace NewDevice.XiWei
{
    /// <summary>
    ///
    /// </summary>
    public class XiWeiFlash
    {
        private SerialProtocolHandler serial;
        private int maxRetryCount = 3;

        /// <summary>
        /// 下载文件单个包大小
        /// </summary>
        private const int CHUNK_SIZE = 58;

        /// <summary>
        /// 外部日志委托
        /// </summary>
        public event Action<string> LogEvent;

        /// <summary>
        /// 报错委托
        /// </summary>
        public event Action<string> ErrorEvent;

        /// <summary>
        /// 文件信息
        /// </summary>
        public List<XiweiFlashInfo> FlashInfos { get; set; } = new List<XiweiFlashInfo>();

        /// <summary>
        ///
        /// </summary>
        public string DeviceName { get; set; }

        /// <summary>
        ///
        /// </summary>
        /// <param name="port"></param>
        /// <param name="baud"></param>
        public void Connect(string port, int baud)
        {
            if (serial == null)
            {
                serial = new SerialProtocolHandler(port, baud);
                serial.OnError += Serial_OnError;
                serial.OnFrameReceived += Serial_OnFrameReceived;
                serial.OnLog += Serial_OnLog;
                serial.Open();
            }
        }

        /// <summary>
        /// 获取所有程序信息
        /// </summary>
        public void GetAllFlashInfo()
        {
            FlashInfos.Clear();
            for (int flashIndex = 0; flashIndex < _flashCount; flashIndex++)
            {
                var totalData = new byte[] { };
                // 只读前两个包
                for (int frameIndex = 0; frameIndex < 2; frameIndex++)
                {
                    byte[] cmd = { 0xA2, 0x06, (byte)flashIndex, (byte)frameIndex };
                    _getFileInfoEvent = new ManualResetEvent(false);

                    int retryTime = 0;
                    for (; retryTime < maxRetryCount; retryTime++)
                    {
                        serial.Send(cmd);
                        if (_getFileInfoEvent.WaitOne(500))   //收到第一个包
                        {
                            if (_currentReadingFlashIndex == flashIndex && _currentReadingFrameIndex == frameIndex) //收到的帧，文件序号，包序号与预期一致
                            {
                                totalData = totalData.Concat(_currentData).ToArray();
                                break;
                            }
                            else
                            {
                            }
                        }
                        else
                        {
                        }
                    }

                    if (retryTime == maxRetryCount)
                    {
                        throw new Exception($"{DeviceName}读取包信息发生错误");
                    }
                }

                var mcuTypeBtyes = totalData.Skip(4).Take(32).ToArray();
                var mcuType = ConvertBytesToAsciiUntilNull(mcuTypeBtyes);
                var fileInfoBytes = totalData.Skip(40 - 4).ToArray();
                var fileInfo = ConvertBytesToAsciiUntilNull(fileInfoBytes);
                var fileNo = int.Parse(fileInfo.Substring(0, 3));
                XiweiFlashInfo flashInfo = new XiweiFlashInfo()
                {
                    FlashName = fileInfo,
                    McuType = mcuType,
                    FlashNo = fileNo
                };
                LogEvent.Invoke($"{DeviceName} 查询到烧录文件[{fileNo}][{fileInfo}][{mcuType}]");
                FlashInfos.Add(flashInfo);
            }
        }

        /// <summary>
        /// 选择烧录文件
        /// </summary>
        /// <param name="flashNo"></param>
        /// <exception cref="Exception"></exception>
        public void SelectFlash(int flashNo)
        {
            if (FlashInfos.Count(x => x.FlashNo == flashNo) != 1)
            {
                throw new Exception($"{DeviceName} 不存在该Flash(no = {flashNo})");
            }

            XiweiFlashInfo flashInfo = FlashInfos.Where(x => x.FlashNo == flashNo).ToArray()[0];

            byte[] cmd = { 0x61, 0x06, (byte)flashNo, 0x00 };

            for (int i = 0; i < maxRetryCount; i++)
            {
                _selectFileEvent = new ManualResetEvent(false);
                serial.Send(cmd);
                if (_selectFileEvent.WaitOne(1000))   //收到选择文件反馈
                {
                    if (_currentData[1] == 0x06) //一般情况下不存在该序号烧录程序时返回长度为6
                    {
                        throw new Exception($"{DeviceName} 切换烧录程序异常，该编号程序{_currentData[2]}不存在");
                    }
                    else
                    {
                        //切换成功
                        _currentFlashName = "";
                        ReadCurrentFlashName(); //会读查询
                        if (_currentFlashName == flashInfo.FlashName)
                        {
                            LogEvent?.Invoke($"{DeviceName}切换文件成功[{flashInfo.FlashName}][{flashInfo.FlashNo}][{flashInfo.McuType}]");
                            return;
                        }
                        else
                        {
                            ErrorEvent?.Invoke($"{DeviceName}切换文件失败 expected[{flashInfo.FlashName}] but[{_currentFlashName}]");
                        }
                    }

                    return;
                }
                else
                {
                }
            }
            throw new Exception($"{DeviceName}选择烧录文件失败");
        }

        private void ReadCurrentFlashName()
        {
            byte[] cmd = { 0xA9, 0x06, 0x00, 0x00 };

            for (int i = 0; i < maxRetryCount; i++)
            {
                _getCurrentFileInfoEvent = new ManualResetEvent(false);
                serial.Send(cmd);
                if (_getCurrentFileInfoEvent.WaitOne(1000))   //收到文件
                {
                    return;
                }
                else
                {
                }
            }
            throw new Exception("获取烧录文件数量失败");
        }

        /// <summary>
        /// 开始烧录
        /// </summary>
        /// <exception cref="Exception"></exception>
        public bool StartFlash()
        {
            flashFinished = false;
            flashReuslt = false;
            _flashStatus = FlashStatus.Reset;
            byte[] cmd = { 0xA8, 0x06, 0x00, 0x00 };

            serial.Send(cmd);

            Stopwatch sw = Stopwatch.StartNew();
            while (sw.Elapsed < TimeSpan.FromSeconds(60))
            {
                if (flashFinished)
                {
                    break;
                }
            }

            if (flashReuslt)
            {
                // 烧录成功
                LogEvent.Invoke($"{DeviceName}烧录成功");
            }
            else
            {
                // 烧录失败
                LogEvent.Invoke($"{DeviceName}烧录失败");
            }
            return flashReuslt;
        }

        /// <summary>
        /// 获取烧录器内部烧录文件数量
        /// </summary>
        public void GetFlashCount()
        {
            byte[] cmd = { 0xA1, 0x06, 0x00, 0x00 };

            _getFileCountEvent = new ManualResetEvent(false);
            for (int i = 0; i < maxRetryCount; i++)
            {
                serial.Send(cmd);
                if (_getFileCountEvent.WaitOne(500))   //收到文件
                {
                    return;
                }
                else
                {
                }
            }
            throw new Exception("获取烧录文件数量失败");
        }

        private void Serial_OnLog(string obj)
        {
            Mylog.Debug($"{DeviceName} {obj} ");
        }

        private int _flashCount;
        private ManualResetEvent _getFileCountEvent;
        private ManualResetEvent _getFileInfoEvent;
        private ManualResetEvent _selectFileEvent;
        private ManualResetEvent _downLoadEvent;
        private ManualResetEvent _downLoadCheckNumsEvent;
        private AutoResetEvent _downLoadResponesEvent;
        private ManualResetEventSlim _downRespones;
        private ManualResetEventSlim _readProgram;
        private ManualResetEvent _getCurrentFileInfoEvent;
        private int _currentReadingFlashIndex;
        private int _currentReadingFrameIndex;
        private byte[] _currentData;
        private string _currentFlashName;
        private FlashStatus _flashStatus;
        private bool flashFinished;
        private bool flashReuslt;

        private void Serial_OnFrameReceived(byte[] obj)
        {
            Mylog.Debug($"{DeviceName} 收到完整帧 [{BitConverter.ToString(obj).Replace("-", " ")}]");
            byte head = obj[0];

            switch (head)
            {
                case 0xB1:  //下位机回复全部文件记录 B1 06 02 00 C6 79
                    _flashCount = obj[2];
                    _getFileCountEvent?.Set();
                    LogEvent.Invoke($"{DeviceName}查询到{_flashCount}个Flash");
                    break;

                case 0xB2:  // 下位机回复文件信息
                    _currentReadingFlashIndex = obj[2];
                    _currentReadingFrameIndex = obj[3];
                    _currentData = obj.Skip(4).Take(64 - 6).ToArray();
                    _getFileInfoEvent?.Set();
                    break;

                case 0x62:  // 切换当前烧录文件返回
                    _currentData = obj;
                    _selectFileEvent?.Set();
                    break;

                case 0xB9:  // 查询当前烧录文件
                    _currentFlashName = ConvertBytesToAsciiUntilNull(_currentData.Skip(4).Take(_currentData.Length - 6).ToArray());
                    _getCurrentFileInfoEvent?.Set();
                    break;

                case 0xB8:  // 确认烧录开始
                    _flashStatus = FlashStatus.ConfirmFlashStart;
                    UpdateFlashResult();
                    break;

                case 0xD2:
                    int sts = obj[2];
                    _flashStatus = (FlashStatus)sts;
                    UpdateFlashResult();
                    break;

                case 0xBB:

                    _downLoadEvent?.Set();

                    break;

                case 0xDD:

                    _downLoadCheckNumsEvent?.Set();

                    break;

                case 0xFF:
                    //确认传输固件数据包命令
                    // _downLoadResponesEvent?.Set();
                    _downRespones.Set();
                    byte[] tail = new byte[] { obj[2], obj[3] };
                    short resultAsShort = BitConverter.ToInt16(tail, 0);
                    //Debug.WriteLine("当前接收到："+resultAsShort);
                    Times.Add(resultAsShort);
                    break;

                case 0x11:
                    //下位机接收完所有的数据包
                    // _downLoadResponesEvent?.Set();
                    _downRespones.Set();
                    byte[] tail2 = new byte[] { obj[2], obj[3] };
                    short resultAsShort2 = BitConverter.ToInt16(tail2, 0);
                    Debug.WriteLine("当前接收到："+resultAsShort2);
                    Times.Add(resultAsShort2);
                    break;

                case 0x22:
                    //下位机烧写完成
                    // _downLoadResponesEvent?.Set();
                    _downRespones.Set();
                    break;

                case 0x3F:
                    if (obj.ToList().Contains(0x55))
                        _readProgram.Set();
                    break;

                case 0x42:
                    var newByte = obj.RemoveDouble(6, 2);
                    ReadList.AddRange(newByte);
                    _readProgram.Set();
                    break;

                default:
                    break;
            }
        }

        private void UpdateFlashResult()
        {
            switch (_flashStatus)
            {
                case FlashStatus.Reset:
                    LogEvent?.Invoke($"{DeviceName}烧录复位");
                    break;

                case FlashStatus.ConfirmFlashStart:
                    LogEvent?.Invoke($"{DeviceName}确认烧录开始");
                    break;

                case FlashStatus.ReadFinishedReady:
                    LogEvent?.Invoke($"{DeviceName}烧录器读出数据，准备烧录");
                    break;

                case FlashStatus.Ready4Flash:
                    LogEvent?.Invoke($"{DeviceName}烧录器准备 OK，可以烧录");
                    break;

                case FlashStatus.UnlockProtect:
                    LogEvent?.Invoke($"{DeviceName}解除读保护");
                    break;

                case FlashStatus.Erasing:
                    LogEvent?.Invoke($"{DeviceName}正在擦除");
                    break;

                case FlashStatus.EraseSuccess:
                    LogEvent?.Invoke($"{DeviceName}擦除成功");
                    break;

                case FlashStatus.Programing:
                    LogEvent?.Invoke($"{DeviceName}正在编程");
                    break;

                case FlashStatus.ProgramSuccess:
                    LogEvent?.Invoke($"{DeviceName}编程成功");
                    break;

                case FlashStatus.Verifying:
                    LogEvent?.Invoke($"{DeviceName}正在校验");
                    break;

                case FlashStatus.VeriftSuccess:
                    flashReuslt = true;
                    flashFinished = true;
                    LogEvent?.Invoke($"{DeviceName}校验成功");
                    break;

                case FlashStatus.ConnectedFailed:
                    flashFinished = true;
                    LogEvent?.Invoke($"{DeviceName}连接失败");
                    break;

                case FlashStatus.EraseFailed:
                    flashFinished = true;
                    LogEvent?.Invoke($"{DeviceName}擦除失败");
                    break;

                case FlashStatus.ProgramFailed:
                    flashFinished = true;
                    LogEvent?.Invoke($"{DeviceName}编程失败");
                    break;

                case FlashStatus.VerifyFailed:
                    flashFinished = true;
                    LogEvent?.Invoke($"{DeviceName}校验失败");
                    break;

                case FlashStatus.FlashCountNone:
                    flashFinished = true;
                    LogEvent?.Invoke($"{DeviceName}剩余烧录次数为 0");
                    break;

                case FlashStatus.VoltageException:
                    flashFinished = true;
                    LogEvent?.Invoke($"{DeviceName}电压异常");
                    break;

                default:
                    break;
            }
        }

        private void Serial_OnError(string obj)
        {
            ErrorEvent?.Invoke($"{DeviceName} {obj} ");
        }

        /// <summary>
        /// Close
        /// </summary>
        public void Close()
        {
            serial?.Close();
        }

        private List<short> Times;

        /// <summary>
        /// 下载程序命令
        /// </summary>
        /// <param name="warePath">ware文件路径</param>
        public void DownLoad(string warePath)
        {
            Times=new List<short>();
            _downRespones =new(false);
            _downLoadResponesEvent =new(false);
            _downLoadEvent =new(false);
            _downLoadCheckNumsEvent =new(false);
            string[] strings = warePath.Split('\\');
            string FileName = strings[strings.Length-1];
            //第一步 传输开始前发送握手数据包，确认存储的ware文件名
            byte[] NameByte = Encoding.UTF8.GetBytes(FileName);
            if (NameByte.Length>32)
            {
                throw new Exception("文件名长度异常");
            }
            int length = NameByte.Length+6;
            byte[] cmd = { 0xAA, Convert.ToByte(length), 0x00, 0xAA };
            serial.Send(cmd.Concat(NameByte).ToArray());
            if (!_downLoadEvent.WaitOne(1000))
            {
                throw new Exception("接收数据超时");
            }

            //确认有多少个数据包
            //读取ware文件
            byte[] allBytes = File.ReadAllBytes(warePath);
            //计算需要发多少次，也就是多少个数据包
            int totalChunks = (int)Math.Ceiling((double)allBytes.Length / CHUNK_SIZE);
            short total = (short)totalChunks;
            // 获取字节数组（注意：BitConverter依赖系统端序）
            byte[] totalbytes = BitConverter.GetBytes(total);
            // 如果系统是大端序（Big-Endian），需要反转数组以得到小端序
            if (BitConverter.IsLittleEndian == false)
            {
                Array.Reverse(totalbytes);
            }
            byte[] cmd2 = { 0xCC, 0x06 };
            serial.Send(cmd2.Concat(totalbytes).ToArray());
            if (!_downLoadCheckNumsEvent.WaitOne(1000))
            {
                throw new Exception("接收数据超时");
            }
            //传输开始
            for (int chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++)
            {
                //计算当前块的起始索引
                int startIndex = chunkIndex * CHUNK_SIZE;
                // 计算当前块的实际长度（最后一个块可能不足58字节）
                int actualChunkSize = Math.Min(CHUNK_SIZE, allBytes.Length - startIndex);

                // 从总数组中提取出当前要发送的数据块
                byte[] currentChunk = new byte[actualChunkSize];
                Array.Copy(allBytes, startIndex, currentChunk, 0, actualChunkSize);
                int onepicLength = currentChunk.Length+6;
                byte[] WhichTimes = BitConverter.GetBytes((short)(chunkIndex));//第N包数据
                byte[] bytes = { 0xEE, Convert.ToByte(onepicLength) };
                byte[] sendBytes = bytes.Concat(WhichTimes).ToArray();
                Mylog.Info(WhichTimes.ToString());
                // 调用发送方法
                serial.Send(sendBytes.Concat(currentChunk).ToArray());

                if (!_downRespones.Wait(2000))
                {
                    throw new Exception("发送数据包超时");
                }
                _downRespones.Reset();
            }
            //传输完毕
            if (!IsConsecutiveAscending(Times)&&totalChunks==(Times.Count+1))
            {
                throw new Exception("数据包发送数量错误！");
            }
            //切换到当前文件
            var nums = StringToByteArrayLittleEndian(FileName.Substring(0, 3));
            byte[] Checkcmd = { 0x61, 0x06 };
            byte[] Final = Checkcmd.Concat(nums).ToArray();
            serial.Send(Final);
        }

        private List<byte> ReadList;

        /// <summary>
        /// 发送读取程序命令
        /// </summary>
        public void ReadProgram(int DataCounts, int PackageSize, string path)
        {
            ReadList=new();
            _readProgram =new(false);
            byte[] cmd = { 0x3E, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x52 ,0x37,0x46, 0x41,0x34,0x54,0x31,0x42,0x42,0x00,0x00, 0x00, 0x00
            ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
            };
            serial.Send(cmd);
            //判断返回
            if (!_readProgram.Wait(1000))
            {
                throw new Exception("发送读取程序失败");
            }
            _readProgram.Reset();
            //确认一共有262144个数据，一次读取56个，分4681个包读取，转化成16进制1249
            for (int i = 0; i<(DataCounts/PackageSize); i++)
            {
                byte[] WhichTimes = BitConverter.GetBytes((short)(i));//第N包数据
                byte[] c1 = { 0x41, 0x40 };
                byte[] c2 = {
                    0x00, 0x00, 0x00 , 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00,0x00
                    ,0x00, 0x00, 0x00 , 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00,0x00
                    ,0x00, 0x00, 0x00 , 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00,0x00
                    ,0x00, 0x00, 0x00 , 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00,0x00
                    ,0x00, 0x00, 0x00 , 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00,0x00
                    ,0x00, 0x00,0x00, 0x00, 0x00 , 0x00, 0x00, 0x00
                    };
                byte[] Finalcmd = c1.Concat(WhichTimes).ToArray();
                serial.Send(Finalcmd.Concat(c2).ToArray());
                //判断返回
                if (!_readProgram.Wait(6000))
                {
                    break;
                }
                _readProgram.Reset();
                Thread.Sleep(1000);
            }

            //输出数据到txt文件

            var data = ReadList.ToArray();
            using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                fileStream.Write(data, 0, data.Length);
            }
        }

        /// <summary>
        /// 判断列表是否连续
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public static bool IsConsecutiveAscending(List<short> list)
        {
            // 处理边界情况：空列表或只有一个元素的列表视为连续
            if (list == null || list.Count <= 1)
                return true;

            // 遍历检查每个元素是否比前一个元素大1
            for (int i = 1; i < list.Count; i++)
            {
                if (list[i] != list[i - 1] + 1)
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 打开ware模板文件
        /// </summary>
        public bool OpenWareFileTemplate(string path)
        {
            return WareGeneratorInterop.openWareTemplate(path)==0 ? true : false;
        }

        /// <summary>
        /// 添加bin文件
        /// </summary>
        /// <param name="path">bin文件地址</param>
        /// <param name="address">文件烧录地址</param>
        /// <param name="type">内存类型</param>
        /// <returns></returns>
        public bool ADDBinFile(string path, int address, MemoryType type)
        {
            BinFileInfo bin = new()
            {
                filePath= path,
                memtype=(uint)type,
                addr =(uint)address
            };
            return WareGeneratorInterop.addBinFile(bin)==0 ? true : false;
        }

        /// <summary>
        /// 保存ware文件，输入指定目录
        /// </summary>
        /// <param name="path">指定目录</param>
        /// <returns></returns>
        public bool SaveWareFile(string path)
        {
            return WareGeneratorInterop.saveWareFile(path) == 0 ? true : false;
        }

        /// <summary>
        /// 获取芯片信息
        /// </summary>
        /// <returns></returns>
        public WareChipInfo GetChipInfo()
        {
            WareChipInfo wareChipInfo = new WareChipInfo();
            if (WareGeneratorInterop.getChipInfo(ref wareChipInfo)!=0)
            {
                throw new Exception("获取芯片信息失败");
            }
            return wareChipInfo;
        }

        /// <summary>
        /// 获取基础配置
        /// </summary>
        /// <returns></returns>
        public WareBasicConfig GetBaseConfig()
        {
            WareBasicConfig wareBasicConfig = new WareBasicConfig();
            if (WareGeneratorInterop.getBasicConfig(ref wareBasicConfig)!=0)
            {
                throw new Exception("获取基础配置失败");
            }
            return wareBasicConfig;
        }

        /// <summary>
        /// 修改基础配置
        /// </summary>
        /// <returns>true 成功</returns>
        public bool SetBaseConfig(WareBasicConfig wareBasicConfig)
        {
            if (WareGeneratorInterop.setBasicConfig(ref wareBasicConfig)!=0)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 清除已经添加的bin文件数据
        /// </summary>
        /// <returns></returns>
        public void ClearBinFile()
        {
            WareGeneratorInterop.clearBinFiles();
        }

        /// <summary>
        /// 获取版本号
        /// </summary>
        /// <returns></returns>
        public string GetDLLVersion()
        {
            return Marshal.PtrToStringAnsi(WareGeneratorInterop.getDLLVersion());
        }

        /// <summary>
        /// 将数字字符串转换为2字节的小端序字节数组
        /// </summary>
        /// <param name="numericString">数字字符串</param>
        /// <returns>2字节的小端序数组</returns>
        public static byte[] StringToByteArrayLittleEndian(string numericString)
        {
            // 参数验证
            if (string.IsNullOrEmpty(numericString))
                throw new ArgumentException("输入字符串不能为空");

            // 1. 将字符串解析为整数
            if (!int.TryParse(numericString, out int number))
                throw new ArgumentException($"'{numericString}' 不是有效的整数");

            // 2. 检查数值范围（适用于2字节无符号整数）
            if (number < 0 || number > 65535) // 0xFFFF
                throw new ArgumentOutOfRangeException("数值必须在0-65535范围内");

            // 3. 转换为小端序字节数组（低位字节在前）
            byte[] bytes = new byte[2];
            bytes[0] = (byte)(number & 0xFF);       // 低字节（低位）
            bytes[1] = (byte)((number >> 8) & 0xFF); // 高字节（高位）

            return bytes;
        }

        /// <summary>
        /// 将字节数组转换为ASCII字符串，遇到第一个null字节(0x00)时停止
        /// </summary>
        /// <param name="bytes">输入的字节数组</param>
        /// <returns>转换后的ASCII字符串</returns>
        private string ConvertBytesToAsciiUntilNull(byte[] bytes)
        {
            if (bytes == null)
                throw new ArgumentNullException(nameof(bytes));

            if (bytes.Length == 0)
                return string.Empty;

            // 查找第一个null字节(0x00)的索引
            int nullIndex = Array.IndexOf(bytes, (byte)0);

            // 如果没有找到null字节，使用整个数组；否则使用null字节前的部分
            int length = nullIndex >= 0 ? nullIndex : bytes.Length;

            // 提取有效部分的字节
            byte[] validBytes = new byte[length];
            Array.Copy(bytes, validBytes, length);

            // 将字节转换为ASCII字符串
            return Encoding.ASCII.GetString(validBytes);
        }
    }
}